﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

using System.Windows.Controls.Primitives;

namespace t01crm.View
{
    // Simple use :
    //
    // XAML:
    //   <Button Commands.Command="{Binding MyCommand}" Content="Click me" /> 
    //
    //  C#: Instanciate a Commend
    //  myCommand = new DelegateCommand<string>(MyCommandExecute /* MyComandExecute is the method to call*/);
    //  myCommand.CanExecute = true;
    //
    //  private void MyCommandExecuted()
    //  {
    //     // this code is called when command is enabled and button was pressed
    //  }

    // More Complex use with command parameter:
    //
    // XAML:
    //   <Button Commands.Command="{Binding MyCommand}"
    //           Commands.CommandParameter="{Binding MyParameter}"
    //           Content="Click me" />
    //
    //  C#: Instanciate a Commend
    //  myCommand = new DelegateCommand<string>(MyCommandExecute /* MyComandExecute is the method to call*/);
    //  myCommand.CanExecute = true;
    //
    //  private void MyCommandExecuted(string parameter)
    //   {
    //            //I could use the MyParameter property in this particular case,
    //            //but this is to show that parameters can be passed to the command.
    //            Message = "Hi " + parameter;
    //   }
    public static class Commands
    {
        private static readonly DependencyProperty CommandButtonBehaviorProperty =
            DependencyProperty.RegisterAttached("CommandButtonBehavior", typeof(CommandButtonBehavior),
                                                typeof(Commands), null);

        public static readonly DependencyProperty CommandProperty =
            DependencyProperty.RegisterAttached("Command", typeof(ICommand), typeof(Commands),
            new PropertyMetadata(CommandPropertyChanged));

        public static readonly DependencyProperty CommandParameterProperty =
            DependencyProperty.RegisterAttached("CommandParameter", typeof(object), typeof(Commands),
            null);

        public static ICommand GetCommand(ButtonBase buttonBase)
        {
            return buttonBase.GetValue(CommandProperty) as ICommand;
        }

        public static void SetCommand(ButtonBase buttonBase, ICommand value)
        {
            buttonBase.SetValue(CommandProperty, value);
        }

        public static object GetCommandParameter(ButtonBase buttonBase)
        {
            return buttonBase.GetValue(CommandParameterProperty);
        }

        public static void SetCommandParameter(ButtonBase buttonBase, object value)
        {
            buttonBase.SetValue(CommandParameterProperty, value);
        }

        private static void CommandPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            ButtonBase element = o as ButtonBase;
            if (element != null)
            {
                if (e.OldValue != null)
                {
                    UnhookCommand(element, e.OldValue as ICommand);
                }
                if (e.NewValue != null)
                {
                    HookCommand(element, e.NewValue as ICommand);
                }
            }
        }

        private static void HookCommand(ButtonBase element, ICommand command)
        {
            CommandButtonBehavior behavior = new CommandButtonBehavior(element, command);
            behavior.Attach();
            element.SetValue(CommandButtonBehaviorProperty, behavior);
        }

        private static void UnhookCommand(ButtonBase element, ICommand command)
        {
            CommandButtonBehavior behavior = (CommandButtonBehavior)element.GetValue(CommandButtonBehaviorProperty);
            behavior.Detach();
            element.ClearValue(CommandButtonBehaviorProperty);
        }

        private class CommandButtonBehavior
        {
            private readonly WeakReference elementReference;
            private readonly ICommand command;

            public CommandButtonBehavior(ButtonBase element, ICommand command)
            {
                this.elementReference = new WeakReference(element);
                this.command = command;
            }

            public void Attach()
            {
                ButtonBase element = GetElement();
                if (element != null)
                {
                    element.Click += element_Clicked;
                    command.CanExecuteChanged += command_CanExecuteChanged;
                    SetIsEnabled(element);
                }
            }

            public void Detach()
            {
                command.CanExecuteChanged -= command_CanExecuteChanged;
                ButtonBase element = GetElement();
                if (element != null)
                {
                    element.Click -= element_Clicked;
                }
            }

            void command_CanExecuteChanged(object sender, EventArgs e)
            {
                ButtonBase element = GetElement();
                if (element != null)
                {
                    SetIsEnabled(element);
                }
                else
                {
                    Detach();
                }
            }

            private void SetIsEnabled(ButtonBase element)
            {
                element.IsEnabled = command.CanExecute(element.GetValue(Commands.CommandParameterProperty));
            }

            private static void element_Clicked(object sender, EventArgs e)
            {
                DependencyObject element = sender as DependencyObject;
                ICommand command = element.GetValue(CommandProperty) as ICommand;
                object commandParameter = element.GetValue(CommandParameterProperty);

                command.Execute(commandParameter);
            }

            private ButtonBase GetElement()
            {
                return elementReference.Target as ButtonBase;
            }
        }
    }
}
